package logger

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"time"

	"gitee.com/night-tc/gobige/threads"
)

type option func(mgr *LoggerMgr)

func LoggerSetConfig(v *LogFileConfig) option {
	return func(mgr *LoggerMgr) {
		mgr.config = v
	}
}

// LoggerMgr 日志管理器，负责日志的生成、写入和管理。
type LoggerMgr struct {
	thgo             *threads.ThreadGo //协程 管理
	config           *LogFileConfig    //日志文件配置
	logFileNowNumber int               //当前日志文件编号(同一天日志超过FileLogMaxLine则会生成新的文件日志)
	logFileNowLine   int               //当前正在写入的日志文件行数
	writeChan        chan *logData     //写日志到文件的通道
	consoleChan      chan *logData     //写日志到控制台通道

	lk         sync.RWMutex
	tmpKeyList []LogKeyEnum //Debug时可以输入日志的Key

	file                *os.File   //普通日志文件
	errorFile           *os.File   //错误日志文件
	fileUpdateTime      *time.Time //普通日志文件更新时间
	errorFileUpdateTime *time.Time //错误日志文件更新时间
}

// newLoggerMgr 创建一个新的日志管理器实例。
// 参数:
//
//	thgo: 协程管理器实例。
//	opts: 可选配置项。
//
// 返回值:
//
//	*LoggerMgr: 日志管理器实例。
func newLoggerMgr(thgo *threads.ThreadGo, opts ...option) (result *LoggerMgr) {
	result = new(LoggerMgr)
	if thgo == nil {
		result.thgo = threads.NewThreadGo()
	} else {
		result.thgo = thgo
	}
	result.config = defaultLogConfig
	result.logFileNowNumber = LOGFILE_INIT_NUMBER
	result.logFileNowLine = 0
	result.writeChan = make(chan *logData, 1000)
	result.consoleChan = make(chan *logData, 1000)
	n := time.Now()
	result.fileUpdateTime = &n
	result.errorFileUpdateTime = &n
	// result.tmpLogLevel = LogLevelDefualt

	for i := range opts {
		opts[i](result)
	}
	result.tmpKeyList = result.config.Keys

	if thgo == nil {
		//如果为nil,表示Test逻辑
		result.writeChan = nil
		result.Start()
	} else {
		result.Start()
	}
	return
}

// Start 启动日志管理器，开启日志写入和控制台输出的协程。
func (this *LoggerMgr) Start() {
	this.thgo.Go(this.run)
	this.thgo.Go(this.runByconsole)
}

// Stop 停止日志管理器，关闭日志写入和控制台输出的通道。
func (this *LoggerMgr) Stop() {
	if this.writeChan != nil {
		close(this.writeChan)
	}
	close(this.consoleChan)
	this.thgo.CloseWait()
}

// 是否有指定日志Key
// 参数:
//
//	key: 日志Key。
//
// 返回值:
//
//	bool: 是否包含指定Key。
func (this *LoggerMgr) checkKey(key LogKeyEnum) bool {
	this.lk.RLock()
	defer this.lk.RUnlock()
	for i := range this.tmpKeyList {
		if this.tmpKeyList[i] == key {
			return true
		}
	}
	return false
}

// 写日志到文件
// 参数:
//
//	key: 日志Key。
//	logLevel: 日志等级。
//	format: 日志内容的格式化字符串。
func (this *LoggerMgr) writeLog(key LogKeyEnum, logLevel logLevel, format string) {
	//生成日志数据
	logData := this.genLogData(key, logLevel, format)
	//通知写入控制台日志的通道
	select {
	case this.consoleChan <- logData:
	default:
	}
	//通知写入日志文件的通道
	select {
	case this.writeChan <- logData:
	default:
	}
}

func getFileNameLineFunc(skipN int) (fName string, line int, funcName string) {
	pc, _, _, ok := runtime.Caller(skipN)
	if !ok {
		fmt.Println("[fatal]cannot get caller info")
		return
	}

	fun := runtime.FuncForPC(pc)
	if fun == nil {
		return
	}
	fName, line = fun.FileLine(pc)
	funcName = fun.Name()
	return
}

// 生成日志数据
// 参数:
//
//	key: 日志Key。
//	logLevel: 日志等级。
//	format: 日志内容的格式化字符串。
//
// 返回值:
//
//	*logData: 日志数据结构。
func (this *LoggerMgr) genLogData(key LogKeyEnum, logLevel logLevel, format string) *logData {
	trace := ""
	if logLevel <= LogLevelError {
		trace = loggerTrace(5)
	}
	ffullpath := "" //长文件名
	fpath := ""     //短文件名
	logTitle := ""
	switch logLevel {
	case LogLevelFatal:
		logTitle = "[FATAL]"
	case LogLevelError:
		logTitle = "[ERROR]"
	case LogLevelWarn:
		logTitle = "[WARN]"
		f, n, _ := getFileNameLineFunc(5)
		ffullpath = fmt.Sprintf(" %v:%v \n", f, n)
	case LogLevelInfo:
		logTitle = "[INFO]"
	case LogLevelDebug:
		logTitle = "[DEBUG]"
	}
	logTime := time.Now().Format("2006-01-02 15:04:05.000")
	var buf bytes.Buffer
	buf.WriteString(logTime)
	buf.WriteString(" ")
	buf.WriteString(logTitle)
	if key != "" {
		buf.WriteString("[")
		buf.WriteString(key)
		buf.WriteString("]")
	}

	//文件行号和函数信息
	if trace != "" {
		//不打印路径，因为有堆栈
	} else if this.config.AppendFileInfo {
		f, n, _ := getFileNameLineFunc(5)
		if f != "" {
			if this.config.TrimFilePath {
				fpath = fmt.Sprintf(" %v:%v ", filepath.Base(f), n)
			} else {
				ffullpath = fmt.Sprintf(" %v:%v \n", f, n)
			}
		}
	}
	buf.WriteString(fpath)
	buf.WriteString(": ")
	buf.WriteString(format)
	buf.WriteString(trace)
	buf.WriteString("\n")
	buf.WriteString(ffullpath)

	return &logData{
		level: logLevel,
		data:  buf.String(),
	}
}

// 打印错误日志到控制台。
// 参数:
//
//	format: 错误信息的格式化字符串。
//	a: 格式化参数。
func (this *LoggerMgr) printError(format string, a ...interface{}) {
	//生成堆栈信息
	trace := loggerTrace(0)
	//格式化错误信息
	err := fmt.Sprintf(format, a...)
	//格式化完整错误信息
	msg := fmt.Sprintf("\n%s [logFile Error]: logFile %s %s error: %s \n %s  \n\n",
		time.Now().Format("2006-01-02 15:04:05"), this.config.LogFilePath, this.config.LogFileName, err, trace)

	//普通打印
	fmt.Print(msg)
}

// fatal 记录致命错误日志。
// 参数:
//
//	format: 日志内容的格式化字符串。
func (this *LoggerMgr) fatal(format string) {
	this.writeLog("", LogLevelFatal, format)
}

// error 记录错误日志。
// 参数:
//
//	format: 日志内容的格式化字符串。
func (this *LoggerMgr) error(format string) {
	this.writeLog("", LogLevelError, format)
}

// warn 记录警告日志。
// 参数:
//
//	format: 日志内容的格式化字符串。
func (this *LoggerMgr) warn(format string) {
	this.writeLog("", LogLevelWarn, format)
}

// info 记录信息日志。
// 参数:
//
//	format: 日志内容的格式化字符串。
func (this *LoggerMgr) info(format string) {
	this.writeLog("", LogLevelInfo, format)
}

// debug 记录调试日志。
// 参数:
//
//	key: 日志的关键字，用于标识日志类别。
//	format: 日志内容的格式化字符串。
func (this *LoggerMgr) debug(key LogKeyEnum, format string) {
	this.writeLog(key, LogLevelDebug, format)
}
